#include <QtGui/QGridLayout>
#include <QtGui/QLabel>
#include <QtGui/QLineEdit>
#include <QtGui/QPushButton>
#include <QtGui/QCheckBox>
#include <QtGui/QTextBrowser>
#include <QtGui/QCloseEvent>
#include "../core/diskdevice.h"
#include "infobutton.h"
#include "choosemountpointwidget.h"
#include "optionsforfswidget.h"
#include "specialcomboboxwidgets.h"
#include "udevrule.h"
#include "usbwizard.h"

UsbWizard::UsbWizard(PopupWindow *popupWindow,QWidget *parent) : QWizard(parent) {
	currentUdevRule = 0;
	
	nextButton = new QPushButton(tr("Next") + " >");
	
	finishButton = new QPushButton(tr("Finish"));
	connect(finishButton,SIGNAL(clicked()),this,SLOT(finish()));
	
	cancelButton = new QPushButton(tr("Cancel"));
	connect(cancelButton,SIGNAL(clicked()),this,SLOT(cancel()));
	
	setButton(QWizard::NextButton,nextButton);
	setButton(QWizard::FinishButton,finishButton);
	setButton(QWizard::CancelButton,cancelButton);
	
	// Create first page "Intruduction"
	introPage = new QWizardPage;
	introPage->setTitle(tr("Introduction"));
	
	introLabel = new QLabel;
	introLabel->setWordWrap(true);
	introLabel->setText(tr("This wizard can help you to set automounting of your usb device. Everything what you have to do it's answer on quastions and after all quastions you will able to see result. On every step you can abort this operation or come back on previous step with help of special button."));
	
	QVBoxLayout *introLayout = new QVBoxLayout;
	introLayout->addWidget(introLabel);
	introLayout->addStretch();
	
	introPage->setLayout(introLayout);
	
	setPage(Introduction,introPage);
	
	// Create page "Choice of the device"
	chooseDevicePage = new QWizardPage;
	chooseDevicePage->setTitle(tr("Choose device to configure it"));
	
	chooseDeviceLabel = new QLabel;
	chooseDeviceLabel->setWordWrap(true);
	chooseDeviceLabel->setText(tr("Choose some device to configure it. All next operations will be done with this device."));
	
	deviceNotConnected = new QRadioButton(tr("Device is not connected"));
	deviceNotConnected->setChecked(true);
	
	QVBoxLayout *chooseDeviceLayout = new QVBoxLayout;
	chooseDeviceLayout->addWidget(chooseDeviceLabel);
	chooseDeviceLayout->addWidget(deviceNotConnected);
	chooseDevicePage->setLayout(chooseDeviceLayout);
	
	setPage(ChooseDevice,chooseDevicePage);
	
	// Create page "Device definition"
	deviceDefinitionPage = new QWizardPage;
	deviceDefinitionPage->setTitle(tr("Device definition"));
	
	deviceDefinitionLabel = new QLabel;
	deviceDefinitionLabel->setWordWrap(true);
	deviceDefinitionLabel->setText(tr("To configure device automounting we have to indicate to operating system what kind of device we want to configure. With help of this fields we can do it. You can read information about every field by click on special button, which is situated near a field."));
	
	deviceName = new QLineEdit;
	vendorName = new QLineEdit;
	modelName = new QLineEdit;
	busName = new QLineEdit;
	subsystemName = new QLineEdit;
	
	deviceNameInfoButton = new InfoButton(popupWindow);
	deviceNameInfoButton->setPopupText(tr("This field is usually used for creation of the rules, which influence on some devices. For example, you can enter sd* - it means that this rule is written for all devices, which have names starting with \"sd\". Other variants: sd? - started with \"sd\" and only one any character after it, sd[a-z][0-9] - started with \"sd\" and after it follows any character for \"a\" to \"z\" and any digit, for example: sda1,sdb1,sdc2 satisfy the conditions."));
	deviceNameInfoButton->setPopupHeaderText(tr("What is device name"));
	
	vendorInfoButton = new InfoButton(popupWindow);
	vendorInfoButton->setPopupText(tr("This thing is used to create the rule for only one device, because in majority of cases vendor is different from USB devices. To define what vendor from your device you have to connect this device and find it in descriptions of your devices from devices tree item of your device."));
	vendorInfoButton->setPopupHeaderText(tr("What is vendor"));
	
	modelInfoButton = new InfoButton(popupWindow);
	modelInfoButton->setPopupText(tr("This this is used to create the rule for only one device or devices of one model."));
	modelInfoButton->setPopupHeaderText(tr("What is model"));
	
	subsystemInfoButton = new InfoButton(popupWindow);
	subsystemInfoButton->setPopupText(tr("When we connect an usb device, Linux kernel and udev connect a lot of inside devices with different subsystems; we always mount devices with \"block\" subsystem, but if you want to see another subsystems run \"udevmonitor --env\" and connect the device."));
	subsystemInfoButton->setPopupHeaderText(tr("What is subsystem"));
	
	busInfoButton = new InfoButton(popupWindow);
	busInfoButton->setPopupText(tr("Through this bus we can get information about device. There are a lot of types of bus and \"usb\" bus is for USB devices."));
	busInfoButton->setPopupHeaderText(tr("What is bus"));
	
	QGridLayout *deviceDefinitionLayout = new QGridLayout;
	deviceDefinitionLayout->addWidget(deviceDefinitionLabel,1,0,1,3);
	deviceDefinitionLayout->addWidget(deviceNameInfoButton,2,0);
	deviceDefinitionLayout->addWidget(new QLabel(tr("Device name")),2,1);
	deviceDefinitionLayout->addWidget(deviceName,2,2);
	deviceDefinitionLayout->addWidget(vendorInfoButton,3,0);
	deviceDefinitionLayout->addWidget(new QLabel(tr("Vendor")),3,1);
	deviceDefinitionLayout->addWidget(vendorName,3,2);
	deviceDefinitionLayout->addWidget(modelInfoButton,4,0);
	deviceDefinitionLayout->addWidget(new QLabel(tr("Model")),4,1);
	deviceDefinitionLayout->addWidget(modelName,4,2);
	deviceDefinitionLayout->addWidget(subsystemInfoButton,5,0);
	deviceDefinitionLayout->addWidget(new QLabel(tr("Subsystem")),5,1);
	deviceDefinitionLayout->addWidget(subsystemName,5,2);
	deviceDefinitionLayout->addWidget(busInfoButton,6,0);
	deviceDefinitionLayout->addWidget(new QLabel(tr("Bus")),6,1);
	deviceDefinitionLayout->addWidget(busName,6,2);
	deviceDefinitionLayout->setColumnStretch(2,1);
	
	deviceDefinitionPage->setLayout(deviceDefinitionLayout);
	
	setPage(DeviceDefinition,deviceDefinitionPage);
	
	// Create page "Mount point"
	mountPointPage = new QWizardPage;
	mountPointPage->setTitle(tr("Configure device mount point"));
	
	mountPointLabel = new QLabel;
	mountPointLabel->setWordWrap(true);
	mountPointLabel->setText(tr("Please, set mount point for the device. You can set fixed mount point, for example \"/media/usb\" or dinamic by using special characters. %k - name of the device, for example if you set mount point \"/media/usb-%k\" and device has name sda1, it will be mounted to \"/media/usb-sda1\"; %n - number of the device, for example for device sda1 and mount point \"/media/myusb_%n\" the device will be mounted to \"/media/myusb_1\"."));
		
	mountPoint = new ChooseMountPointWidget;
	mountPoint->setReadOnly(false);
	
	removeMountPoint = new QCheckBox(tr("Remove mount point after device disconnection"));
	
	QVBoxLayout *mountPointLayout = new QVBoxLayout;
	mountPointLayout->addWidget(mountPointLabel);
	mountPointLayout->addWidget(mountPoint);
	mountPointLayout->addWidget(removeMountPoint);
	
	mountPointPage->setLayout(mountPointLayout);
	
	setPage(MountPoint,mountPointPage);
	
	// Create page "Options for automounting"
	optionsPage = new QWizardPage;
	optionsPage->setTitle(tr("Configure automounting options"));
	
	optionsLabel = new QLabel;
	optionsLabel->setWordWrap(true);
	optionsLabel->setText(tr("USB devices can have different file systems and every file system have own options, that's why we have to indicate to operating system with what options device must be mounted. Here you can add file systems and set options for them by click on special button with \"plus\" icon. You can use main window of MountManager to define a options string. If you have ideas how to relieve this process, write about it to main developer e-mail: sstikhonov@gmail.com. Thank you."));
	
	optionsForFSWidget = new OptionsForFSWidget;
	
	QVBoxLayout *optionsLayout = new QVBoxLayout;
	optionsLayout->addWidget(optionsLabel);
	optionsLayout->addWidget(optionsForFSWidget);
	optionsLayout->addStretch();
	
	optionsPage->setLayout(optionsLayout);

	setPage(Options,optionsPage);
	
	// Create page "Access options"
	accessOptionsPage = new QWizardPage;
	accessOptionsPage->setTitle(tr("Access options"));
	
	accessOptionsLabel = new QLabel;
	accessOptionsLabel->setWordWrap(true);
	accessOptionsLabel->setText(tr("This options can indicate to operating system who have access to the device. You can choose device group and owner. Only that users who are part of the chosen group and owner can manipulate the device files. "));
		
	groupName = new GIDWidget;
	ownerName = new UIDWidget;
	
	QGridLayout *accessOptionsLayout = new QGridLayout;
	accessOptionsLayout->addWidget(accessOptionsLabel,0,0,1,2);
	accessOptionsLayout->addWidget(new QLabel(tr("Group")),1,0);
	accessOptionsLayout->addWidget(groupName,1,1);
	accessOptionsLayout->addWidget(new QLabel(tr("Owner")),2,0);
	accessOptionsLayout->addWidget(ownerName,2,1);
	accessOptionsLayout->setColumnStretch(1,1);
	
	accessOptionsPage->setLayout(accessOptionsLayout);
	
	setPage(AccessOptions,accessOptionsPage);

	// Create page "Result"
	resultPage = new QWizardPage;
	resultPage->setTitle(tr("Results"));
	
	resultViewer = new QTextBrowser;
	
	QVBoxLayout *resultLayout = new QVBoxLayout;
	resultLayout->addWidget(resultViewer);
	
	resultPage->setLayout(resultLayout);

	setPage(Result,resultPage);

	setWindowTitle(tr("USB Wizard"));
	resize(600,400);
	
	connect(this,SIGNAL(currentIdChanged(int)),this,SLOT(currentPageChanged(int)));
}

UsbWizard::~UsbWizard() {
	delete nextButton;
	delete cancelButton;
	delete finishButton;
	
	delete introLabel;
	delete chooseDeviceLabel;
	delete deviceNotConnected;
	
	delete deviceNameInfoButton;
	delete vendorInfoButton;
	delete modelInfoButton;
	delete busInfoButton;
	delete subsystemInfoButton;
	
	delete deviceName;
	delete vendorName;
	delete modelName;
	delete busName;
	delete subsystemName;
	
	delete optionsForFSWidget;
	delete optionsLabel;
	
	delete accessOptionsLabel;
	delete groupName;
	delete ownerName;
	
	delete resultViewer;
	
	chooseDevices.clear();
}

void UsbWizard::setDefaultDevice(DiskDevice *device) {
	foreach (DeviceView *deviceView,chooseDevices)
		if (deviceView->core() == device) {
			deviceView->setChecked(true);
			restart();
			next();
			next();
		}
}

void UsbWizard::editUdevRule(UdevRule *udevRule) {
	currentUdevRule = udevRule;
	restart();
}

void UsbWizard::setUsbDevices(const QList<DiskDevice *>& devices) {
	foreach (DiskDevice *device,devices) {
		if (device->isDisk())
			continue;
		DeviceView *deviceView = new DeviceView(device,QString(device->blockName()).remove("/dev/"));
		chooseDevicePage->layout()->addWidget(deviceView);
		chooseDevices << deviceView;
	}
}
		
void UsbWizard::setMountOptionsForDevice(DiskDevice *device,const QString& mountOptions) {
	foreach (DeviceView *deviceView,chooseDevices)
		if (deviceView->core() == device)
			deviceView->setMountOptions(mountOptions);
}
		
void UsbWizard::setMountPointForDevice(DiskDevice *device,const QString& mountPoint) {
	foreach (DeviceView *deviceView,chooseDevices)
		if (deviceView->core() == device)
			deviceView->setMountPoint(mountPoint);
}

void UsbWizard::addUsbDevice(DiskDevice *addedDevice) {
	if (addedDevice->isDisk())
		return;
	foreach (DeviceView *deviceView,chooseDevices)
		if (deviceView->core() == addedDevice)
			return;
	DeviceView *deviceView = new DeviceView(addedDevice,QString(addedDevice->blockName()).remove("/dev/"));
	chooseDevicePage->layout()->addWidget(deviceView);
	chooseDevices << deviceView;
}

void UsbWizard::removeUsbDevice(DiskDevice *removedDevice) {
	if (removedDevice->isDisk())
		return;
	for (int i = chooseDevices.count() - 1; i >= 0; i--)
		if (chooseDevices[i]->core() == removedDevice)
			delete chooseDevices.takeAt(i);
}

void UsbWizard::currentPageChanged(int id) {
	bool wasChoice = false;
	if (deviceNotConnected->isChecked() || chosenDeviceView != 0)
		wasChoice = true;
	if (id == Introduction && currentUdevRule != 0)
		next();
	else if (id == ChooseDevice && currentUdevRule != 0)
		next();
	else if (id == DeviceDefinition) {
		if (currentUdevRule != 0) {
			setDefaultDeviceDefinition = true;
			setDefaultMountPoint = true;
			setDefaultOptions = true;
			setDefaultAccessOptions = true;
		} else {
			
			DeviceView *currentDeviceView = 0;
			foreach (DeviceView *deviceView,chooseDevices)
				if (deviceView->isChecked())
					currentDeviceView = deviceView;
			if (currentDeviceView != chosenDeviceView) {
				chosenDeviceView = currentDeviceView;
				setDefaultDeviceDefinition = true;
				setDefaultMountPoint = true;
				setDefaultOptions = true;
				setDefaultAccessOptions = true;
			}
				
			if (!wasChoice)
				back();
		}
		
		if (setDefaultDeviceDefinition) {
			
			// Set values to form
			if (currentUdevRule != 0) {
				deviceName->setText(currentUdevRule->getName());
				vendorName->setText(currentUdevRule->getVendor());
				modelName->setText(currentUdevRule->getModel());
				busName->setText(currentUdevRule->getBus());
				subsystemName->setText(currentUdevRule->getSubsystem());
			} else if (chosenDeviceView == 0) {	
				deviceName->clear();
				vendorName->clear();
				modelName->clear();
				busName->clear();
				subsystemName->clear();
			} else {	
				deviceName->setText(QString(chosenDeviceView->core()->blockName()).remove("/dev/"));
				vendorName->setText(chosenDeviceView->core()->vendor());
				modelName->setText(chosenDeviceView->core()->model());
				busName->setText(chosenDeviceView->core()->bus());
				subsystemName->setText("block");
			}
			
			setDefaultDeviceDefinition = false;
		}
		deviceName->setFocus();
	} else if (id == MountPoint) {
		if (setDefaultMountPoint) {
			if (currentUdevRule != 0) {
				mountPoint->setMountPoint(currentUdevRule->getMountPoint());
				removeMountPoint->setChecked(currentUdevRule->getRemoveMountPoint());
			}
			else if (chosenDeviceView == 0)
				mountPoint->setMountPoint("");
			else
				mountPoint->setMountPoint(chosenDeviceView->getMountPoint());
			setDefaultMountPoint = false;
		}
	} else if (id == Options) {
		if (!mountPoint->isValid()) {
			back();
			mountPoint->setFocus();
		}
		
		if (setDefaultOptions) {
			optionsForFSWidget->clear();
			if (currentUdevRule != 0) {
				QMap<QString,QString> optionsWithFS = currentUdevRule->getOptionsWithFileSystems();
				for (QMap<QString,QString>::const_iterator i = optionsWithFS.constBegin(); i != optionsWithFS.constEnd(); ++i)
					optionsForFSWidget->addRow(i.key(),i.value());
			} else if (chosenDeviceView != 0) {
				optionsForFSWidget->addRow(chosenDeviceView->core()->fileSystem(),chosenDeviceView->getMountOptions());
			} else
				optionsForFSWidget->addRow();
			setDefaultOptions = false;
		}
	} else if (id == AccessOptions) {
		if (setDefaultAccessOptions) {
			groupName->setCurrentIndex(0);
			ownerName->setCurrentIndex(0);
			if (currentUdevRule != 0) {
				for (int i = 0; i < groupName->count(); i++)
					if (groupName->itemText(i) == currentUdevRule->getGroup())
						groupName->setCurrentIndex(i);
				
				for (int i = 0; i < ownerName->count(); i++)
					if (ownerName->itemText(i) == currentUdevRule->getOwner())
						ownerName->setCurrentIndex(i);
			} 
			setDefaultAccessOptions = false;
		}
	} else if (id == Result) {
		QString resultContent;
		
		resultContent += "<ol><li>";
		if (currentUdevRule != 0)
			resultContent += tr("You have edited device");
		else {
			resultContent += tr("You have chosen device");
			if (chosenDeviceView == 0)
				resultContent += ", " + tr("which isn't connected");
			else
				resultContent += " " + tr("with name") + " <b>" + 
							QString(chosenDeviceView->core()->blockName()).remove("/dev/") + "</b>";
		}
		resultContent += "</li>";
		
		resultContent += "<li>";
		resultContent += tr("This rule is written only for devices with:") + "<br>";
		resultContent += "<b>" + tr("Device name") + "</b>: ";
		resultContent += deviceName->text().isEmpty() ? tr("Any") : deviceName->text();
		resultContent += "<br><b>" + tr("Vendor") + "</b>: ";
		resultContent += vendorName->text().isEmpty() ? tr("Any") : vendorName->text();
		resultContent += "<br><b>" + tr("Model") + "</b>: ";
		resultContent += modelName->text().isEmpty() ? tr("Any") : modelName->text();
		resultContent += "<br><b>" + tr("Bus") + "</b>: ";
		resultContent += busName->text().isEmpty() ? tr("Any") : busName->text();
		resultContent += "<br><b>" + tr("Subsystem") + "</b>: ";
		resultContent += subsystemName->text().isEmpty() ? tr("Any") : subsystemName->text();
		resultContent += "</li>";
		
		resultContent += "<li>";
		resultContent += tr("Device content will be mounted to directory") + " <b>" + mountPoint->mountPoint() + ".</b> ";
		resultContent += removeMountPoint->isChecked() ? tr("And the directory will be removed when device disconnects.") : "";
		resultContent += "</li>";
		
		resultContent += "<li>" + tr("Options of automounting for file systems:") + "<br>";
		for (int i = 0; i < optionsForFSWidget->count(); i++)
			resultContent += "<b>" + optionsForFSWidget->getFileSystem(i) + "</b>: " + optionsForFSWidget->getOptions(i) + "<br>";
		resultContent += "</li>";
		
		resultContent += "<li>" + tr("Only these users have access to the device:") + "<br>";
		resultContent += tr("Group") + ": <b>" + groupName->itemText(groupName->currentIndex()) + "</b><br>";
		resultContent += tr("Owner") + ": <b>" + ownerName->itemText(ownerName->currentIndex()) + "</b></li>";
		resultContent += "</ol>";
		
		resultViewer->setHtml(resultContent);
	}
}

void UsbWizard::cancel() {
	currentUdevRule = 0;
	setDefaultDeviceDefinition = true;
	setDefaultMountPoint = true;
	setDefaultOptions = true;
	setDefaultAccessOptions = true;
}

void UsbWizard::finish() {
	UdevRule *rule = currentUdevRule == 0 ? new UdevRule : currentUdevRule;
	rule->setName(deviceName->text());
	rule->setVendor(vendorName->text());
	rule->setModel(modelName->text());
	rule->setBus(busName->text());
	rule->setSubsystem(subsystemName->text());
	rule->setMountPoint(mountPoint->mountPoint());
	rule->setRemoveMountPoint(removeMountPoint->isChecked());
	rule->setGroup(groupName->currentIndex() == 0 ? "" : groupName->itemText(groupName->currentIndex()));
	rule->setOwner(ownerName->currentIndex() == 0 ? "" : ownerName->itemText(ownerName->currentIndex()));
	rule->clearOptionsWithFileSystems();
	for (int i = 0; i < optionsForFSWidget->count(); i++)
		rule->addOptionsWithFileSystem(optionsForFSWidget->getFileSystem(i),optionsForFSWidget->getOptions(i));
	if (currentUdevRule != 0)
		emit (ruleEdited(rule));
	else
		emit (ruleCreated(rule));
	currentUdevRule = 0;
}

void UsbWizard::closeEvent(QCloseEvent *event) {
	cancel();
	event->accept();
}
